package grpcPool

import (
	"context"
	"errors"
	"fmt"
	"github.com/sirupsen/logrus"
	"google.golang.org/grpc"
	"time"
)

func NewConn[T any](address string, fn func(connInterface grpc.ClientConnInterface) T, opts ...grpc.DialOption) T {
	var (
		v   T
		err error
		c   *grpc.ClientConn
	)

	if c, err = grpc.DialContext(context.Background(), address, opts...); err != nil {
		logrus.Warnf("grpc dial[%s] err: %v", address, err)
		return v
	}

	return fn(c)
}

func DoRequest[R any, RR any](address string, service *grpc.ServiceDesc, method interface{}, ctx context.Context, in *R, opts ...grpc.DialOption) (*RR, error) {
	var (
		err        error
		conn       *grpc.ClientConn
		fullMethod string
		resp       = new(RR)
	)

	if service == nil || method == nil {
		return resp, errors.New("service or method is nil")
	}

	switch method.(type) {
	case *grpc.MethodDesc:
		fullMethod = fmt.Sprintf("/%s/%s", service.ServiceName, method.(*grpc.MethodDesc).MethodName)
	case grpc.MethodDesc:
		fullMethod = fmt.Sprintf("/%s/%s", service.ServiceName, method.(grpc.MethodDesc).MethodName)
	case string:
		mstr := method.(string)
		found := false

		for idx := range service.Methods {
			if service.Methods[idx].MethodName == mstr {
				found = true
				break
			}
		}

		if !found {
			return resp, fmt.Errorf("service method[%s] not found", mstr)
		}

		fullMethod = fmt.Sprintf("/%s/%s", service.ServiceName, mstr)
	default:
		return resp, errors.New("method type not allowed")
	}

	timeoutCtx, _ := context.WithTimeout(context.Background(), 10*time.Second)

	if conn, err = grpc.DialContext(timeoutCtx, address, opts...); err != nil {
		return resp, err
	}
	defer conn.Close()

	if err = conn.Invoke(ctx, fullMethod, in, resp); err != nil {
		return resp, err
	}

	return resp, nil
}
